#pragma once

#include <iostream>
#include <string>
#include "Sock.hpp"
#include <system_error>
#include <array>
#include <algorithm>
#include <functional>

namespace select_ns
{

    const int default_port = 8080;
    const int fd_size = sizeof(fd_set) * 8;
    const int default_fd = -1;

    using func_t = std::function<std::string(const std::string&)>;

    class SelectServer
    {
    public:
        SelectServer(func_t func, int port = default_port)
            : _port(port), _listensock(-1), _func(func)
        {
        }

        ~SelectServer()
        {
            if (_listensock > 0)
                close(_listensock);
        }

        void Accepter()
        {
            std::string clientip;
            uint16_t clientport;
            int sock = Sock::Accept(_listensock, &clientip, &clientport);
            if (sock < 0)
            {
                return;
            }

            // 将用户的 文件描述符 保存
            int i = 0;
            for (i = 0; i < _fdarray.size(); ++i)
            {
                if (_fdarray[i] != default_fd)
                    continue;
                else
                    break;
            }
            if (i == _fdarray.size())
            {
                logMessage(WARNING, "server is full, plase wait at time");
                close(sock);
            }
            else
            {
                _fdarray[i] = sock;
            }
        }

        void Recver(int sock, int pos)
        {
            // 1. 读取报文，保证完整读到一整个报文（暂时不处理
            char buffer[1024];
            ssize_t n = recv(sock, buffer, sizeof(buffer)-1, 0);
            if (n > 0)
            {
                buffer[n-1] = 0;//去除换行符
                logMessage(NORMAL, "client # %s", buffer);
            }
            else if (n == 0)
            {
                close(sock);
                _fdarray[pos] = default_fd;
                logMessage(NORMAL, "client quit");
                return ;
            }
            else 
            {
                close(sock);
                _fdarray[pos] = default_fd;
                std::system_error erro(errno, std::generic_category());
                logMessage(ERROR, "recverro: %d, %s", erro.code().value(), erro.what());
            }

            // 2. 处理请求 request -> respond
            std::string respond = _func(buffer);

            // 3. send
        }

        void HandlerEvent(fd_set &rfds)
        {

            // 若有多个文件描述符就绪，处理已经就绪的文件描述符
            for (int pos = 0; pos<_fdarray.size(); ++pos)
            {
                // 过滤非法fd
                if (_fdarray[pos] == default_fd)
                    continue;

                if (_fdarray[pos] == _listensock && FD_ISSET(_listensock, &rfds)) // 如果监听事件就绪
                {
                    Accepter();
                }
                else if (FD_ISSET(_fdarray[pos], &rfds)) // 如果其他事件就绪
                {
                    Recver(_fdarray[pos], pos);
                }
            }
        }

        void initServer()
        {
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);
            _fdarray.fill(default_fd);
            _fdarray[0] = _listensock;
        }

        void start()
        {

            while (true)
            {
                fd_set rfds;
                FD_ZERO(&rfds);

                int maxfd = _fdarray[0];
                // 将合法的文件描述符添加到fd_set中
                for (auto fd : _fdarray)
                {
                    if (fd == default_fd)
                        continue;
                    FD_SET(fd, &rfds);

                    maxfd = std::max(maxfd, fd);
                }

                struct timeval timeout = {1, 0}; // {秒, 微秒} 1s内阻塞式，超过1s，非阻塞返回1次
                // int selret = select(_listensock + 1, &rfds, nullptr, nullptr, &timeout);
                int selret = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr); // 阻塞式调用

                std::system_error erro(errno, std::generic_category());
                switch (selret)
                {
                case 0: // 超时返回
                    logMessage(NORMAL, "timeout...");
                    break;
                case -1: // 错误返回
                    logMessage(WARNING, "select err, errCode: %d, %s", erro.code().value(), erro.what());
                    break;
                default:
                    logMessage(NORMAL, "get a new link...");
                    HandlerEvent(rfds);
                    break;
                }
            }
        }

    private:
        int _port;
        int _listensock;
        std::array<int, fd_size> _fdarray;
        func_t _func;
    };
}